package io.gitee.zhangbinhub.admin.authentication

import io.gitee.zhangbinhub.acp.boot.log.LogAdapter
import io.gitee.zhangbinhub.admin.base.BaseAuthenticationProvider
import io.gitee.zhangbinhub.admin.component.AuthPasswordEncrypt
import io.gitee.zhangbinhub.admin.component.AuthTokenService
import io.gitee.zhangbinhub.admin.component.AuthUserService
import io.gitee.zhangbinhub.admin.component.TokenTools
import io.gitee.zhangbinhub.admin.constant.OauthConstant
import io.gitee.zhangbinhub.admin.entity.User
import io.gitee.zhangbinhub.admin.vo.TokenUserInfoVo
import org.springframework.security.core.Authentication
import org.springframework.security.core.AuthenticationException
import org.springframework.security.oauth2.core.*
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2ClientAuthenticationToken
import org.springframework.security.oauth2.server.authorization.token.OAuth2TokenGenerator

class OauthUserPasswordAuthenticationProvider(
    private val logAdapter: LogAdapter,
    private val tokenTools: TokenTools,
    private val authUserService: AuthUserService,
    private val authPasswordEncrypt: AuthPasswordEncrypt,
    private val tokenGenerator: OAuth2TokenGenerator<out OAuth2Token?>,
    private val authTokenService: AuthTokenService
) : BaseAuthenticationProvider {
    @Throws(AuthenticationException::class)
    override fun authenticate(authentication: Authentication): Authentication {
        val oauthUserPasswordAuthenticationRequest = authentication as OauthUserPasswordAuthenticationRequest
        val user = validateUser(oauthUserPasswordAuthenticationRequest)
        val clientPrincipal: OAuth2ClientAuthenticationToken =
            oauthUserPasswordAuthenticationRequest.clientPrincipal as OAuth2ClientAuthenticationToken
        val registeredClient = getRegisteredClient(clientPrincipal)
        // 强制删除之前的token
        authTokenService.removeTokensByClientIdAndUserName(registeredClient.id, user.loginNo)
        // 获取 builder
        val authorizationBuilder = getAuthorizationBuilder(
            registeredClient,
            AuthorizationGrantType(OauthConstant.granterUserPassword),
            clientPrincipal,
            user.loginNo
        )
        return generateToken(
            registeredClient,
            clientPrincipal,
            oauthUserPasswordAuthenticationRequest,
            AuthorizationGrantType(OauthConstant.granterUserPassword),
            authorizationBuilder,
            tokenGenerator,
            authUserService.loadUserAuthorities(user),
            tokenTools.encryptUserInfo(
                TokenUserInfoVo(
                    appId = registeredClient.clientId,
                    id = user.id,
                    loginNo = user.loginNo,
                    name = user.name,
                    mobile = user.mobile,
                    loginTime = System.currentTimeMillis()
                )
            ),
            oauthUserPasswordAuthenticationRequest.additionalParameters
        ).apply {
            authTokenService.save(authorizationBuilder.build())
        }
    }

    override fun supports(authentication: Class<*>): Boolean {
        return OauthUserPasswordAuthenticationRequest::class.java.isAssignableFrom(authentication)
    }

    @Throws(OAuth2AuthenticationException::class)
    fun validateUser(oauthUserPasswordAuthenticationRequest: OauthUserPasswordAuthenticationRequest): User =
        (oauthUserPasswordAuthenticationRequest.principal as? String)?.let { username ->
            authUserService.loadUserByUsername(username).apply {
                if (!this.enabled) {
                    logAdapter.error("用户【${this.loginNo}】已被锁定或禁用！")
                    throw OAuth2AuthenticationException(
                        OAuth2Error(
                            OAuth2ErrorCodes.INVALID_REQUEST, "用户【${this.loginNo}】已被锁定或禁用！", null
                        )
                    )
                }
                (oauthUserPasswordAuthenticationRequest.credentials as? String)?.let { password ->
                    if (!authPasswordEncrypt.matches(password, this.password)) {
                        authUserService.storePasswordErrorTime(username)
                        logAdapter.error("【${username}】用户名或密码不正确！")
                        throw OAuth2AuthenticationException(
                            OAuth2Error(
                                OAuth2ErrorCodes.INVALID_REQUEST, "【${username}】用户名或密码不正确！", null
                            )
                        )
                    } else {
                        authUserService.clearPasswordErrorTime(username)
                    }
                } ?: throw OAuth2AuthenticationException(
                    OAuth2Error(
                        OAuth2ErrorCodes.INVALID_REQUEST, "密码不能为空！", null
                    )
                )
            }
        } ?: throw OAuth2AuthenticationException(
            OAuth2Error(
                OAuth2ErrorCodes.INVALID_REQUEST, "用户名不能为空！", null
            )
        )
}